---
title: Your first API route
---


::doc-component-demo
---
title: Define your first event handler
---

Similar to pages, you can define event handlers in your `app/api` directory. These event handlers are automatically registered as API routes by Nitro.

:doc-heading{label="Hello world route"}

We will create a `/api/hello` route that will return a greeting message.

```ts [app/api/hello.ts]
export default defineEventHandler((event) => {
  // get the query parameters from the event
  const query = getQuery(event)
  // extract the "from" query parameter
  const from = query.from || 'tairo'

  return `Hello ${from}!`
})
```

You can test your API route by visiting `http://localhost:3000/api/hello?from=world` in your browser or by using `curl`:

```bash
curl -i http://localhost:3000/api/hello?from=world
# Access-Control-Allow-Origin: *
# content-type: text/html
# date: Wed, 19 Apr 2023 18:03:31 GMT
# connection: close
# content-length: 12
#
# Hello world!
```


:doc-heading{label="Dynamic routes"}

You can also define dynamic routes by using the `[paramName]` syntax in either files or directories. For example, if you want to create a route that will have a dynamic `id` parameter, you can create a file named `app/api/product/[id].ts` or a directory named `[id]` like `app/api/product/[id]/index.ts`.

In addition to parameters, you can add suffixes to your dynamic routes. For example, you can create a route that will only accept `POST` requests by creating a file ending with `.post.ts`.


```ts [app/api/add-to-card/[id].post.ts]
export default defineEventHandler(await (event) => {
  // get the id parameters from the context
  const { id } = event.context.params

  // read and parse the request body
  const body = await readBody(event)

  // add the product to the cart (this is just an example)
  const cartItem = await addToCart(id, body.quantity)

  // perform other actions ...

  // returning an object will automatically set 
  // the content-type: application/json header
  return cartItem
})
```

---

Useful resources:

- [Learn about server directory on nuxt.com](https://nuxt.com/docs/guide/directory-structure/server)
- [Learn about API Routes on nitro.unjs.io](https://nitro.unjs.io/guide/routing)
- [Discover h3 server utils on github.com](https://github.com/unjs/h3#utilities)

::


::doc-component-demo
---
title: Connect your data
code: false
---

Now that you have a page and an API route, you can connect them together. We will request the hello API route from the page created [in the previous section](/documentation/setup/first-page) and display the result.

```vue [app/pages/index.vue]
<script setup lang="ts">
// definePageMeta ...

// create a reactive input value
const input = ref('')

// create a computed object that will be updated
// every time the input value changes
const query = computed(() => ({ from: input.value }))

// call our API route, it will be re-fetched
// every time the query object changes
// note that data type is detected automatically!
const { data } = useFetch('/api/hello', {
  query,
})
</script>

<template>
  <div class="max-w-lg">
    <BaseCard class="p-6">
      <!-- reactive input -->
      <BaseInput 
        label="Your name" 
        v-model="input"
      />

      <!-- display the result -->
      <BaseParagraph class="mt-4 line-clamp-1 max-w-sm">
        {{ data }}
      </BaseParagraph>
    </BaseCard>
  </div>
</template>
```

We've seen that using the :doc-linker{to="BaseInput"}, :doc-linker{to="BaseCard"} and :doc-linker{to="BaseParagraph"} components was very easy. You may also want to take a look at how we do [form validation](/documentation/guides/form-validation) and explore form pages from the demo to learn more about how to build forms with Tairo.

---

Useful resources:

- [Read data fetching guide on nuxt.com](https://nuxt.com/docs/getting-started/data-fetching)
::


::doc-nav
---
prev: /documentation/setup/first-page
next: /documentation/setup/add-a-layout
---
::